/*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <sourcemod>
#include <sdktools>
#include <sdkhooks>

#include "macros.sp"
#include "helpers.sp"

#define NUMBER_OF_CAMPAIGNS 14
#define NUMBER_OF_CUSTOM 1		/* COSTUM MAP TO ADD IN THE VOTE */
#define NUMBER_OFFICIAL 13              /* TOTAL - COSTUM */
#define MAX_ITEM_TO_VOTE 3
#define MAX_LAST_MAP 3

#define SOUND_NEW_VOTE_START	"ui/Beep_SynthTone01.wav"
#define SOUND_NEW_VOTE_WINNER	"ui/alert_clink.wav"

/*maps */
new String:strCampaignFirstMap[NUMBER_OF_CAMPAIGNS][32];		//Array of maps to switch to
new String:strCampaignLastMap[NUMBER_OF_CAMPAIGNS][32];		//Array of maps to switch from
new String:strCampaignName[NUMBER_OF_CAMPAIGNS][32];			//Array of names of the campaign

new Handle:cvar_timevote = INVALID_HANDLE;
new Handle:cvar_timeresult = INVALID_HANDLE;


new voted[MAXCLIENTS] = { -1, ... };
new bool:first_round = true;
new bool:already_display[NUMBER_OF_CAMPAIGNS];
new bool:can_vote = true;
new bool:on_change = false;
new bool:will_change = false;

new winner;
new choosed_map[MAX_ITEM_TO_VOTE];
new last_maps[MAX_LAST_MAP] = { -1, ... };
new last_map_index = 0;

new String:next_map[32];


new Handle:hmenu_vote[MAXCLIENTS];

new Handle:timer_to_result;

public OnPluginStart()
{

	cvar_timevote = CreateConVar("l4d2_votemap_timevote", "60", "How many time before display vote", CVAR_FLAGS);
	cvar_timeresult = CreateConVar("l4d2_votemap_timeresult", "60", "How many time before vote is closed", CVAR_FLAGS);

	//First Maps of the Campaign
	Format(strCampaignFirstMap[0], 32, "c8m1_apartment");
	Format(strCampaignFirstMap[1], 32, "c1m1_hotel");
	Format(strCampaignFirstMap[2], 32, "c7m1_docks");
	Format(strCampaignFirstMap[3], 32, "c6m1_riverbank");
	Format(strCampaignFirstMap[4], 32, "c2m1_highway");
	Format(strCampaignFirstMap[5], 32, "c3m1_plankcountry");
	Format(strCampaignFirstMap[6], 32, "c4m1_milltown_a");
	Format(strCampaignFirstMap[7], 32, "c5m1_waterfront");
	Format(strCampaignFirstMap[8], 32, "c13m1_alpinecreek");	
	Format(strCampaignFirstMap[9], 32, "c10m1_caves");
	Format(strCampaignFirstMap[10], 32, "c11m1_greenhouse");
	Format(strCampaignFirstMap[11], 32, "c12m1_hilltop");
	Format(strCampaignFirstMap[12], 32, "c9m1_alleys");

	//custom
	Format(strCampaignFirstMap[13], 32, "l4d_deathaboard01_prison");
	
	//Last Maps of the Campaign
	Format(strCampaignLastMap[0], 32, "c8m5_rooftop");
	Format(strCampaignLastMap[1], 32, "c1m4_atrium");
	Format(strCampaignLastMap[2], 32, "c7m3_port");
	Format(strCampaignLastMap[3], 32, "c6m3_port");
	Format(strCampaignLastMap[4], 32, "c2m5_concert");
	Format(strCampaignLastMap[5], 32, "c3m4_plantation");
	Format(strCampaignLastMap[6], 32, "c4m5_milltown_escape");
	Format(strCampaignLastMap[7], 32, "c5m5_bridge");
	Format(strCampaignLastMap[8], 32, "c13m4_cutthroatcreek");
	Format(strCampaignLastMap[9], 32, "c10m5_houseboat");
	Format(strCampaignLastMap[10], 32, "c11m5_runway");
	Format(strCampaignLastMap[11], 32, "C12m5_cornfield");
	Format(strCampaignLastMap[12], 32, "c9m2_lots");

	//custom
	Format(strCampaignLastMap[13], 32, "l4d_deathaboard05_light");

	//Campaign Names
	Format(strCampaignName[0], 32, "No Mercy");
	Format(strCampaignName[1], 32, "Dead Center");
	Format(strCampaignName[2], 32, "The Sacrifice");
	Format(strCampaignName[3], 32, "The Passing");
	Format(strCampaignName[4], 32, "Dark Carnival");
	Format(strCampaignName[5], 32, "Swamp Fever");
	Format(strCampaignName[6], 32, "Hard Rain");
	Format(strCampaignName[7], 32, "The Parish");
	Format(strCampaignName[8], 32, "Cold Stream");
	Format(strCampaignName[9], 32, "Death Toll");
	Format(strCampaignName[10], 32, "Dead Air");
	Format(strCampaignName[11], 32, "Blood Harvest");
	Format(strCampaignName[12], 32, "Crash Course");

	//Custom map
	Format(strCampaignName[13], 32, "*Death Aboard");


	HookEvent("round_start", Event_OnRoundStart);
	HookEvent("round_end", Event_OnRoundEnd, EventHookMode_Pre);

}

public OnMapStart()
{
	//Precache sounds
	PrecacheSound(SOUND_NEW_VOTE_START);
	PrecacheSound(SOUND_NEW_VOTE_WINNER);
}

public OnMapEnd()
{
	first_round = true; //called at each map change
}

public Event_OnRoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{

	new map_id = is_last_map();
        if(map_id != -1) //last map
	{
		if(first_round) //1st round
		{
			first_round = false;
			will_change = false;
			make_selection(map_id);
			clear_vote();
			CreateTimer(GetConVarFloat(cvar_timevote), call_vote, _, TIMER_FLAG_NO_MAPCHANGE);
		
		}
		else //2nd round
		{
			will_change = true;
			CreateTimer(GetConVarFloat(cvar_timevote), display_next_map, _, TIMER_FLAG_NO_MAPCHANGE);
		}
	}
	else
	{
		will_change = false;
	}

}

public Action:display_next_map(Handle:timer, any:junk)
{
	EmitSoundToAll(SOUND_NEW_VOTE_WINNER);
	PrintToChatAll("\x01Next map is \x03%s", strCampaignName[choosed_map[winner]]);
}

public Event_OnRoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
	if(will_change)
	{
		if(can_vote) //timer to result not expired yet
		{
			KillTimer(timer_to_result);
			calculate_next_map();		
		}

		if(!on_change)
		{		
			on_change = true;
			PrintToChatAll("\x01Changing to \x03%s\x01...", strCampaignName[choosed_map[winner]]);
			CreateTimer(12.0, change_map, _, TIMER_FLAG_NO_MAPCHANGE);
		}
	}
}

public Action:change_map(Handle:timer, any:junk)
{
	ServerCommand("changelevel %s", next_map);	//Change the campaign
}

public is_last_map()
{
	decl String:strCurrentMap[32];
	GetCurrentMap(strCurrentMap,32);

	for(new i=0; i < NUMBER_OF_CAMPAIGNS; i++)
	{
		if(strcmp(strCurrentMap, strCampaignLastMap[i]) == 0)
		{
			return i;
		}
	}

	return -1;
}

clear_vote()
{
	for(new i=0; i < MAXCLIENTS; i++)
	{
		voted[i] = -1;
	}
	
	can_vote = true;
	on_change = false;
}

public Action:call_vote(Handle:timer, any:junk)
{
	if(!can_vote)
		return;

	FOR_EACH_HUMAN(client)
	{
		VoteMenuDraw(client);
	}

	timer_to_result = CreateTimer(GetConVarFloat(cvar_timeresult), resultat, _, TIMER_FLAG_NO_MAPCHANGE);
}

/*choose 5 random official campaign and 1 custom */
public make_selection(current_map)
{

	/*clean array*/
	for(new clean=0; clean < NUMBER_OFFICIAL; clean++)
	{
		already_display[clean] = false;
	}

	/* set a random map + the custom map : 8 elem max, so 5 random + 1 costum + I don't care + title*/
	for(new iCampaign = 0; iCampaign < MAX_ITEM_TO_VOTE; iCampaign++)
	{
		new display_map = GetRandomInt(0,(NUMBER_OFFICIAL -1));
		while (already_display[display_map] || display_map == current_map || in_last_maps(display_map))
		{
			display_map = (display_map + 1) % NUMBER_OFFICIAL; /*should never block cause more 6 official campaign*/
		}

		already_display[display_map] = true;
		push_map(display_map);
		choosed_map[iCampaign] = display_map;      
	}
}


//Draw the menu for voting
public Action:VoteMenuDraw(iClient)
{
	if(!IsValidHumanClient(iClient))
		return Plugin_Handled;
	
	//Create the menu
	hmenu_vote[iClient] = CreateMenu(VoteMenuHandler);
	
	//Give the player the option of not choosing a map
	AddMenuItem(hmenu_vote[iClient], "option1", "I Don't Care");
	
	SetMenuTitle(hmenu_vote[iClient], "Vote for the next campaign\n ");

	for(new iCampaign = 0; iCampaign < MAX_ITEM_TO_VOTE; iCampaign++)
	{
		AddMenuItem(hmenu_vote[iClient], strCampaignName[choosed_map[iCampaign]], strCampaignName[choosed_map[iCampaign]]);
	}
	
	//Add an exit button
	SetMenuExitButton(hmenu_vote[iClient], true);
	
	//And finally, show the menu to the client
	DisplayMenu(hmenu_vote[iClient], iClient, MENU_TIME_FOREVER);
	
	//Play a sound to indicate that the user can vote on a map
	EmitSoundToClient(iClient, SOUND_NEW_VOTE_START);
	
	return Plugin_Handled;
}


//Handle the menu selection the client chose for voting
public VoteMenuHandler(Handle:hMenu, MenuAction:maAction, iClient, iItemNum)
{
	if(!IsValidHumanClient(iClient))
		return;

	if(!can_vote)
	{
		PrintHintText(iClient, "Sorry, vote is closed!");
		return;
	}
	if(maAction == MenuAction_Select) 
	{
		//Set the players current vote
		if(iItemNum == 0)
		{
			voted[iClient] = -1;
		}
		else
		{
			voted[iClient] = iItemNum - 1;
			PrintHintText(iClient, "You voted for %s", strCampaignName[choosed_map[iItemNum - 1]]);
		}				

		CloseHandle(hmenu_vote[iClient]);
	}

}

public Action:resultat(Handle:timer, any:junk)
{
	calculate_next_map();
}

calculate_next_map()
{
	can_vote = false;

	new result[MAX_ITEM_TO_VOTE] = { 0, ... };
	
	/* count all votes */
	for(new i=1; i< MAXCLIENTS; i++)
	{
		if(voted[i] != -1)
		{
			sub_debug("voted for %d", voted[i]);
			result[voted[i]]++;
		}
	}

	/* who is the winner */
	winner = 0;
	new second = 1;
	new max = 0;
	new second_votes = 0;
	for(new i=0; i < MAX_ITEM_TO_VOTE; i++)
	{
		if(result[i] > max)
		{
			/*the old winner is now 2nd (if it's not the first - aka 0 - in this case we keep 1 in second)*/
			second = (i == 0 ? 1 : winner);
			second_votes = max;

			max = result[i];
			winner = i;
		}
		else
		{
			if(result[i] > second_votes)
			{
				second = i;
				second_votes = result[i];
			}
		}
	}

	sub_debug("winner : %d", winner);

	EmitSoundToAll(SOUND_NEW_VOTE_WINNER);
	PrintToChatAll("\x01Le winning : \x03%s\x01! (%d votes)", strCampaignName[choosed_map[winner]], max);
	PrintToChatAll("\x01+-- Almost won : %s... (%d votes)", strCampaignName[choosed_map[second]], second_votes);
	strcopy(next_map, sizeof(next_map), strCampaignFirstMap[choosed_map[winner]]);

}


push_map(mapnum)
{
	last_maps[last_map_index] = mapnum;
	last_map_index = (last_map_index + 1) % MAX_LAST_MAP;
}

bool:in_last_maps(mapnum)
{
	for(new i=0; i < MAX_LAST_MAP; i++)
	{
		if(last_maps[i] == mapnum)
			return true;
	}

	return false;
}